﻿using Common;
using Common.FrontEnd;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Util.Props;

namespace FrontEnd.Feature
{
    /// <summary>
    /// Implements a linear feature transformation transformation.
    ///
    /// It might be a dimension reduction or just a decorrelation transform. This
    /// component requires a special model trained with LDA/MLLT transform.
    /// </summary>
    public class FeatureTransform: BaseDataProcessor
    {
        /**
        /// The name of the transform matrix file.
         */
        [S4Component(type = typeof(ILoader))]
        public static String PROP_LOADER = "loader";

        float[][] transform;
        protected ILoader loader;

        int rows;
        int values;

        public FeatureTransform(ILoader loader) 
        {
            init(loader);
        }

        public FeatureTransform() {
        }

        /*
        /// (non-Javadoc)
        /// 
        /// @see
        /// edu.cmu.sphinx.util.props.Configurable#newProperties(edu.cmu.sphinx.util
        /// .props.PropertySheet)
         */
        override
        public void newProperties(PropertySheet ps)
        {
            base.newProperties(ps);
            init((ILoader) ps.getComponent(PROP_LOADER));
        }

        private void init(ILoader loader) 
        {
            this.loader = loader;

            try {
                loader.load();
            } 
            catch (Exception e) 
            {
                Trace.TraceError(e.ToString());
            }

            transform = loader.getTransformMatrix();
        }

        /**
        /// Returns the next Data object being processed by this LDA, or if it is a
        /// Signal, it is returned without modification.
        /// 
        /// @return the next available Data object, returns null if no Data object is
        ///         available
        /// @throws DataProcessingException
        ///             if there is a processing error
        /// @see Data
         */
        public IData getData()
        {
            IData data = getPredecessor().getData();

            if (null == transform || null == data || !(data is FloatData))
                return data;

            FloatData floatData = (FloatData) data; 
            float[] features = floatData.getValues();

            if (features.Length > transform[0].Length + 1)
                throw new ArgumentException("dimenstion mismatch");

            float[] result = new float[transform.Length];
            getTimer().start();

            for (int i = 0; i < transform.Length; ++i) 
            {
                for (int j = 0; j < features.Length; ++j)
                    result[i] += transform[i][j]*features[j];
            }

            if (features.Length > transform[0].Length) 
            {
                for (int i = 0; i < transform.Length; ++i)
                    result[i] += transform[i][features.Length];
            }

            getTimer().stop();

            return new FloatData(result,
                                 floatData.getSampleRate(),
                                 floatData.getFirstSampleNumber());
        }

    }
}
